package com.write.ibatis.session;

import com.write.ibatis.annotation.LFInsert;
import com.write.ibatis.annotation.LFSelect;
import com.write.ibatis.config.Configuration;
import com.write.ibatis.config.JdbcProperties;
import com.write.ibatis.config.MapperStatement;
import com.write.ibatis.session.impl.DefaultSqlSessionFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.*;

public class SqlSessionFactoryBuilder {

    public SqlSessionFactory build(InputStream inputStream){
        //读取核心配置文件的并进行解析
        SAXReader reader = new SAXReader();
        Configuration configuration = new Configuration();
        Document document = null;
        try {
            document = reader.read(inputStream);
            //获取xml文档树
            Element rootElement = document.getRootElement();
            //获取properties元素
            Element propertiesEl = rootElement.element("properties");
            String jdbcPropertiesPatch = propertiesEl.attributeValue("resource").trim();
            //获取jdbc properties信息，并封装到Configuration对象中的jdbcproperties属性下
            loadProperties(configuration,jdbcPropertiesPatch);

            //读取mapper
            Element mappers = rootElement.element("mappers");
            List<Element> mapper = mappers.elements("mapper");
            for (Element element : mapper) {
                String mapperPath = element.attributeValue("resource");
                loadMapperStatement(configuration,mapperPath);
            }
            //基于中注解开发解析
            Element scanPackage = mappers.element("package");
            if(scanPackage!=null){
                String packagePath = scanPackage.attributeValue("name");
                loadAnnotationMapperStatement(configuration,packagePath);
            }

        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return new DefaultSqlSessionFactory(configuration);
    }

    private void loadAnnotationMapperStatement(Configuration configuration, String packagePath) {
        //根据包获取包下所有的接口集合
        Set<String> fileNames=getInterfaceNamesByPackage(packagePath);
        for (String typeName : fileNames) {
            Class<?> type = null;
            try {
                type = Class.forName(typeName);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            Method[] methods = type.getMethods();
            for (Method method : methods) {
                //查询
                LFSelect selectAnno = method.getDeclaredAnnotation(LFSelect.class);
                if(selectAnno!=null){
                    String selectSql = selectAnno.value();
                    MapperStatement ms = new MapperStatement();
                    ms.setSql(selectSql);
                    ms.setId(typeName+"."+method.getName());
                    ms.setQueryType("select");
                    Class<?> returnType = method.getReturnType();
                    if(Collection.class.isAssignableFrom(returnType)){
                        Type genericReturnType = method.getGenericReturnType();
                        if(genericReturnType instanceof ParameterizedType){
                            Type[] actualTypeArguments = ((ParameterizedType)genericReturnType).getActualTypeArguments();
                            Type t=actualTypeArguments[0];
                            System.out.println(t.getTypeName());
                            System.out.println(1);
                            ms.setResultType(t.getTypeName());
                        }
                    }else{
                        ms.setResultType(returnType.getCanonicalName());
                    }
                    configuration.addMapperStatement(ms);
                    continue;
                }
                //插入
                LFInsert insertAnnotation = method.getAnnotation(LFInsert.class);
                if(insertAnnotation!=null){
                    MapperStatement ms = new MapperStatement();
                    String insertSql = insertAnnotation.value();
                    ms.setSql(insertSql);
                    ms.setId(typeName+"."+method.getName());
                    ms.setQueryType("insert");
                   configuration.addMapperStatement(ms);
                }
                //other TODO

            }
        }

    }

    private Set<String> getInterfaceNamesByPackage(String packageName) {
        HashSet<String> nameSet = new HashSet<>();
        Enumeration<URL> urlEnumeration = null;
        try {
            urlEnumeration = Thread.currentThread().
                    getContextClassLoader().getResources(packageName.replace(".", "/"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        while (urlEnumeration.hasMoreElements()){
            URL url = urlEnumeration.nextElement();
            System.out.println(url.toString());
            if(url!=null && url.getProtocol().equals("file")){
                String path = url.getPath();
                System.out.println(path);
                File[] files = new File(path).listFiles(new FileFilter() {
                    @Override
                    public boolean accept(File file) {
                        return (file.isFile() && file.getName().endsWith(".class"));
                    }
                });
                for (File file : files) {
                    String className = file.getName();
                    className= className.substring(0,className.lastIndexOf("."));
                    className=packageName+"."+className;
                    System.out.println(className);
                    nameSet.add(className);
                }
            }
        }
        return nameSet;
    }

    /**
     * 加载mapper xml文件
     * @param configuration
     * @param mapperPath
     */
    private void loadMapperStatement(Configuration configuration, String mapperPath) {
        InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(mapperPath);
        Document mapperDoc = null;
        try {
            mapperDoc = new SAXReader().read(in);
            Element rootElement = mapperDoc.getRootElement();
            String namespace = rootElement.attributeValue("namespace");
            List<Element> els = rootElement.elements();
            for (Element el : els) {
                MapperStatement mapperStatement = new MapperStatement();
                String queryType = el.getName();
                mapperStatement.setQueryType(queryType);
                String id = el.attributeValue("id");
                mapperStatement.setId(namespace+"."+id);
                String resultType = el.attributeValue("resultType");
                mapperStatement.setResultType(resultType);
                String sql = el.getText();
                mapperStatement.setSql(sql);
                configuration.addMapperStatement(mapperStatement);
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    /**
     * 加载jdbcproperties文件
     * @param configuration
     * @param jdbcPropertiesPatch
     */
    private void loadProperties(Configuration configuration, String jdbcPropertiesPatch)  {
        InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(jdbcPropertiesPatch);
        Properties p = new Properties();
        try {
            p.load(in);
        } catch (IOException e) {
            e.printStackTrace();
        }
        JdbcProperties jp = new JdbcProperties();
        jp.setDriver(p.getProperty("driver"));
        jp.setPassword(p.getProperty("password"));
        jp.setUrl(p.getProperty("url"));
        jp.setUsername(p.getProperty("username"));
        configuration.setJdbcProperties(jp);
    }

}
